﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace PrisonStep
{
    public class PieFire
    {
        /// <summary>
        /// The current game
        /// </summary>
        PrisonGame game;

        public class Pie
        {
            /// <summary>
            /// The current transform
            /// </summary>
            private Matrix transform;

            /// <summary>
            /// The velocity of the pie
            /// </summary>
            private float velocity = 0;

            /// <summary>
            /// Stores which pie to display
            /// </summary>
            private int pieDisplay;

            /// <summary>
            /// Indicates whether or not the pie is currently in the barrel of the bazooka
            /// </summary>
            private bool inBarrel = true;

            /// <summary>
            /// Indicates whether or not the pie is stuck to a wall
            /// </summary>
            private bool isStuck = false;

            private float radius = 7.2f;
            public float Radius { get { return radius; } set { radius = value; } }

            /// <summary>
            /// The velocity of the pie
            /// </summary>
            public float Velocity { get { return velocity; } set { velocity = value; } }

            /// <summary>
            /// The current transformation
            /// </summary>
            public Matrix Transform { get { return transform; } set { transform = value; } }

            /// <summary>
            /// The pie to display
            /// </summary>
            public int PieDisplay { get { return pieDisplay; } set { pieDisplay = value; } }

            public bool InBarrel { get { return inBarrel; } set { inBarrel = value; } }
            public bool IsStuck { get { return isStuck; } set { isStuck = value; } }
        }
        /// <summary>
        /// The model we loaded
        /// </summary>
        private Model model;

        /// <summary>
        /// The pies which currently exist
        /// </summary>
        private LinkedList<Pie> pies = new LinkedList<Pie>();

        /// <summary>
        /// The next pie to make
        /// </summary>
        private int nextPieToMake = 1;

        private PhibesModel phibesModel;

        private Dictionary<int, HashSet<String>> pieMeshesToShow =
            new Dictionary<int, HashSet<String>>();

        public PieFire(PrisonGame game, PhibesModel phibesModel)
        {
            this.game = game;
            this.phibesModel = phibesModel;

            pieMeshesToShow.Add(1, new HashSet<String>(new String[] { "Pie1" }));
            pieMeshesToShow.Add(2, new HashSet<String>(new String[] { "Pie2top01", "Pie2bot01" }));
            pieMeshesToShow.Add(3, new HashSet<String>(new String[] { "Pie3bot", "Pie3top" }));
        }

        public void LoadContent(ContentManager content)
        {
            model = content.Load<Model>("pies");
        }

        public void Update(GameTime gametime, Matrix bazookaPosition, bool isOpen, Alien alien, Gort gort, Dalek dalek, Player victoria)
        {
            
            // Update the position
            // Loop over all laser blasts in the list
            for (LinkedListNode<Pie> pieNode = pies.First; pieNode != null; )
            {
                LinkedListNode<Pie> nextPie = pieNode.Next;

                // This is the actual pie object we are working on 
                Pie pie = pieNode.Value;

                //if the pie is in the barrel
                if (pie.InBarrel)
                {
                    //offset pie by correct amount
                    //pie 1 does not change
                    //pie 2 -10 in Y
                    //pie 3 -20 in Y
                    float tmp = (pie.PieDisplay - 1) * -10;
                    Vector3 pieOffset = Vector3.TransformNormal(new Vector3(0, 1, 0), bazookaPosition) * tmp;
                    Matrix piePosition = bazookaPosition * Matrix.CreateTranslation(pieOffset);
                    pie.Transform = piePosition;
                }

                //otherwise move the pie
                else if (!pie.IsStuck)
                {
                    Vector3 offset = Vector3.TransformNormal(new Vector3(0, 1, 0), pie.Transform)
                        * pie.Velocity * (float)gametime.ElapsedGameTime.TotalSeconds;
                    Vector3 pieDir = offset;
                    pieDir.Normalize();

                    //check if the pie will hit alien
                    if (alien.testCollisionRay(pie.Transform.Translation, pieDir, pie.Radius))
                    {
                        //change the pie's direction
                        int handbone = alien.AnimatedModel.Model.Bones["Bip01 L Hand"].Index;
                        Vector3 desiredDirection = (alien.AnimatedModel.GetBoneAbsoluteTransform(handbone)
                            * alien.Transform).Translation - pie.Transform.Translation;

                        desiredDirection.Normalize();
                        Matrix newDir = new Matrix();
                        newDir.Backward = desiredDirection;
                        newDir.Up = new Vector3(0, 0, 1);
                        newDir.Right = Vector3.Cross(newDir.Up, desiredDirection);
                        newDir.Right.Normalize();
                        newDir.Translation = pie.Transform.Translation;
                        newDir.M44 = 1;     //this line is required
                        newDir = Matrix.CreateRotationX((float)Math.PI / 2) * newDir;
                        pie.Transform = newDir;
                    }
                    //check if the pie will hit gort
                    else if (gort.testCollisionRay(pie.Transform.Translation, pieDir, pie.Radius))
                    {
                        //change the pie's direction
                        int handbone = gort.AnimatedModel.Model.Bones["Bip01 L Hand"].Index;
                        Vector3 desiredDirection = (gort.AnimatedModel.GetBoneAbsoluteTransform(handbone)
                            * gort.Transform).Translation - pie.Transform.Translation;

                        desiredDirection.Normalize();
                        Matrix newDir = new Matrix();
                        newDir.Backward = desiredDirection;
                        newDir.Up = new Vector3(0, 0, 1);
                        newDir.Right = Vector3.Cross(newDir.Up, desiredDirection);
                        newDir.Right.Normalize();
                        newDir.Translation = pie.Transform.Translation;
                        newDir.M44 = 1;     //this line is required
                        newDir = Matrix.CreateRotationX((float)Math.PI / 2) * newDir;
                        pie.Transform = newDir;
                    }

                    //move the pie                    
                    pie.Transform = pie.Transform * Matrix.CreateTranslation(offset);

                    //detect what region the pie is in
                    String region = phibesModel.TestRegion(pie.Transform.Translation);
                    //determine if the pie hit a wall, ceiling or floor
                    if (pie.Transform.Translation.Y < 0 || pie.Transform.Translation.Y > 250 ||
                        (!isOpen && region.StartsWith("R_D")) ||
                        !region.StartsWith("R"))
                    {
                        pie.IsStuck = true;
                        pie.Velocity = 0;
                    }

                    //determine if the pie hit the alien
                    //only allow one pie to hit the alien at a time
                    //get 1000 points for hitting the alien
                    if (alien.Pie == null && alien.testCollisionSphere(pie.Radius, pie.Transform.Translation))
                    {
                        victoria.GameScore += 1000;
                        alien.Pie = pie;
                        pies.Remove(pieNode);
                    }

                    //determine if the pie hit Gort
                    //only allow one pie to hit the alien at a time
                    //get 1000 points for hitting the alien
                    else if (gort.Pie == null && gort.testCollisionSphere(pie.Radius, pie.Transform.Translation))
                    {
                        victoria.GameScore += 1000;
                        gort.Pie = pie;
                        pies.Remove(pieNode);
                    }

                    //determine if the pie hit Dalek
                    //simply remove the pie
                    else if (dalek.testCollisionSphere(pie.Radius, pie.Transform.Translation))
                    {
                        victoria.GameScore += 1000;
                        pies.Remove(pieNode);
                    }
                }

                pieNode = nextPie;
            }
        }

        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            foreach (Pie pie in pies)
            {
                DrawModel(graphics, pie.Transform, pie.PieDisplay);
            }
        }

        public void DrawModel(GraphicsDeviceManager graphics, Matrix world, int display)
        {

            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            foreach (ModelMesh mesh in model.Meshes)
            {
                HashSet<String> meshesToDraw = pieMeshesToShow[display];

                //only draw the meshes associated with the current pie
                if (!meshesToDraw.Contains(mesh.Name.ToString()))
                    continue;

                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index] * world;
                    effect.View = game.Camera.View;
                    effect.Projection = game.Camera.Projection;
                }
                mesh.Draw();
            }
        }

        /// <summary>
        /// This function fires the pie that is currently in the barrel
        /// </summary>
        /// <returns></returns>
        public void Fire()
        {
            if (pies.Count > 0 && pies.Last.Value.InBarrel)
            {
                pies.Last.Value.InBarrel = false;
                pies.Last.Value.Velocity = 300f;
            }
        }

        /// <summary>
        /// This function loads a pie in the pie bazooka
        /// </summary>
        public void LoadPie()
        {
            if ((pies.Count > 0 && !pies.Last.Value.InBarrel) || pies.Count == 0) {
                Pie pie = new Pie();
                pie.PieDisplay = nextPieToMake;
                pies.AddLast(pie);
                nextPieToMake = (nextPieToMake + 1) % 3 + 1;
            }
        }
    }
}
